๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ ์๋๋ฆฌ์ค์์ ๋ณต์กํ ๋ก๋ฉ ์ํ ๊ณ์ธต ๊ตฌ์กฐ๋ฅผ ๋ง๋ค๊ณ ์ฌ์ฉ์ ๊ฒฝํ์ ํฅ์์ํค๊ธฐ ์ํ React Suspense fallback chain์ ํ์ํฉ๋๋ค. ๋ชจ๋ฒ ์ฌ๋ก ๋ฐ ๊ณ ๊ธ ๊ธฐ์ ์ ๋ฐฐ์๋๋ค.
React Suspense Fallback Chain: ๊ฒฌ๊ณ ํ ๋ก๋ฉ ์ํ ๊ณ์ธต ๊ตฌ์กฐ ๊ตฌ์ถ
React Suspense๋ React 16.6์ ๋์ ๋ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ์ผ๋ก, ์ผ๋ฐ์ ์ผ๋ก API์์ ๊ฐ์ ธ์จ ๋ฐ์ดํฐ์ธ ์ข ์์ฑ์ด ๋ก๋๋ ๋๊น์ง ์ปดํฌ๋ํธ ๋ ๋๋ง์ "์ผ์ ์ค์ง"ํ ์ ์์ต๋๋ค. ์ด๋ฅผ ํตํด ๋ก๋ฉ ์ํ๋ฅผ ์ฐ์ํ๊ฒ ๊ด๋ฆฌํ๊ณ ์ฌ์ฉ์ ๊ฒฝํ์ ๊ฐ์ ํ ์ ์์ผ๋ฉฐ, ํนํ ์ฌ๋ฌ ๋ฐ์ดํฐ ์ข ์์ฑ์ด ์๋ ๋ณต์กํ ์ ํ๋ฆฌ์ผ์ด์ ์์ ์ ์ฉํฉ๋๋ค. ํนํ ์ ์ฉํ ํจํด์ fallback chain์ผ๋ก, ๋ฐ์ดํฐ๊ฐ ๋ก๋๋๋ ๋์ ํ์ํ fallback ์ปดํฌ๋ํธ์ ๊ณ์ธต ๊ตฌ์กฐ๋ฅผ ์ ์ํฉ๋๋ค. ์ด ๋ธ๋ก๊ทธ ๊ฒ์๋ฌผ์์๋ React Suspense fallback chain์ ๊ฐ๋ ์ ์ดํด๋ณด๊ณ ๊ตฌํ์ ์ํ ์ค์ฉ์ ์ธ ์์ ์ ๋ชจ๋ฒ ์ฌ๋ก๋ฅผ ์ ๊ณตํฉ๋๋ค.
React Suspense ์ดํดํ๊ธฐ
fallback chain์ ๋ํด ์์ธํ ์์๋ณด๊ธฐ ์ ์ React Suspense์ ํต์ฌ ๊ฐ๋ ์ ๊ฐ๋ตํ๊ฒ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
React Suspense๋ ๋ฌด์์ธ๊ฐ์?
React Suspense๋ ์ปดํฌ๋ํธ๊ฐ ๋ ๋๋ง๋๊ธฐ ์ ์ "๋ฌด์ธ๊ฐ"๋ฅผ "๊ธฐ๋ค๋ฆด" ์ ์๋๋ก ํ๋ ๋ฉ์ปค๋์ฆ์ ๋๋ค. ์ด "๋ฌด์ธ๊ฐ"๋ ์ผ๋ฐ์ ์ผ๋ก ๋น๋๊ธฐ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ์ด์ง๋ง, ์ด๋ฏธ์ง ๋ก๋ฉ ๋๋ ์ฝ๋ ๋ถํ ๊ณผ ๊ฐ์ ๋ค๋ฅธ ๋น๋๊ธฐ ์์ ์ผ ์๋ ์์ต๋๋ค. ์ปดํฌ๋ํธ๊ฐ ์ผ์ ์ค์ง๋๋ฉด React๋ ํด๋น ์ปดํฌ๋ํธ๊ฐ ๊ธฐ๋ค๋ฆฌ๋ promise๊ฐ ํด๊ฒฐ๋ ๋๊น์ง ์ง์ ๋ fallback UI๋ฅผ ๋ ๋๋งํฉ๋๋ค.
Suspense์ ์ฃผ์ ๊ตฌ์ฑ ์์
<Suspense>: ์ผ์ ์ค์ง๋ ์ปดํฌ๋ํธ์ ๊ฒฝ๊ณ๋ฅผ ์ ์ํ๊ณ fallback UI๋ฅผ ์ง์ ํ๋ ๋ํผ ์ปดํฌ๋ํธ์ ๋๋ค.fallbackprop: ์ปดํฌ๋ํธ๊ฐ ์ผ์ ์ค์ง๋๋ ๋์ ํ์ํ UI์ ๋๋ค. ๊ฐ๋จํ ๋ก๋ฉ ์คํผ๋๋ถํฐ ๋ ๋ณต์กํ ํ๋ ์ด์คํ๋๊น์ง ๋ชจ๋ React ์ปดํฌ๋ํธ๊ฐ ๋ ์ ์์ต๋๋ค.- ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ: Suspense๋
react-query,swr๊ณผ ๊ฐ์ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋๋ Fetch API ๋ฐ Promises๋ฅผ ์ง์ ํ์ฉํ์ฌ ๋ฐ์ดํฐ๊ฐ ์ค๋น๋์์์ ์๋ฆฌ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์ ์๋ํฉ๋๋ค.
๊ธฐ๋ณธ Suspense ์์
๋ค์์ React Suspense์ ๊ธฐ๋ณธ ์ฌ์ฉ๋ฒ์ ๋ณด์ฌ์ฃผ๋ ๊ฐ๋จํ ์์ ์ ๋๋ค.
import React, { Suspense } from 'react';
function fetchData() {
return new Promise(resolve => {
setTimeout(() => {
resolve('Data loaded!');
}, 2000);
});
}
const resource = {
data: null,
read() {
if (this.data) {
return this.data;
}
throw fetchData().then(data => {
this.data = data;
});
},
};
function MyComponent() {
const data = resource.read();
return <p>{data}</p>;
}
function App() {
return (
<Suspense fallback={<p>Loading...</p>}
<MyComponent />
</Suspense>
);
}
export default App;
์ด ์์ ์์ MyComponent๋ ๋ฐ์ดํฐ๊ฐ ์์ง ์ฌ์ฉ ๊ฐ๋ฅํ์ง ์์ ๋ promise๋ฅผ throwํ๋ resource ๊ฐ์ฒด(๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ ์์
์ ์๋ฎฌ๋ ์ด์
)๋ฅผ ์ฌ์ฉํฉ๋๋ค. <Suspense> ์ปดํฌ๋ํธ๋ ์ด promise๋ฅผ catchํ๊ณ promise๊ฐ ํด๊ฒฐ๋๊ณ ๋ฐ์ดํฐ๊ฐ ์ฌ์ฉ ๊ฐ๋ฅํด์ง ๋๊น์ง "Loading..." fallback์ ํ์ํฉ๋๋ค. ์ด ๊ธฐ๋ณธ ์์ ๋ ํต์ฌ ์์น์ ๊ฐ์กฐํฉ๋๋ค. React Suspense๋ฅผ ์ฌ์ฉํ๋ฉด ์ปดํฌ๋ํธ๊ฐ ๋ฐ์ดํฐ ๋ก๋๋ฅผ ๊ธฐ๋ค๋ฆฌ๊ณ ์์์ ์๋ฆฌ๊ณ ๋ก๋ฉ ์ํ๋ฅผ ํ์ํ๋ ๊น๋ํ ๋ฐฉ๋ฒ์ ์ ๊ณตํฉ๋๋ค.
Fallback Chain ๊ฐ๋
fallback chain์ <Suspense> ์ปดํฌ๋ํธ์ ๊ณ์ธต ๊ตฌ์กฐ๋ก, ๊ฐ ์์ค์ ์ ์ง์ ์ผ๋ก ๋ ์์ธํ๊ฑฐ๋ ์ธ๋ จ๋ ๋ก๋ฉ ์ํ๋ฅผ ์ ๊ณตํฉ๋๋ค. ์ด๋ UI์ ๋ค๋ฅธ ๋ถ๋ถ์ด ๋ก๋ฉ ์๊ฐ์ด ๋ค๋ฅด๊ฑฐ๋ ์ข
์์ฑ์ด ๋ค๋ฅผ ์ ์๋ ๋ณต์กํ ์ฌ์ฉ์ ์ธํฐํ์ด์ค์ ํนํ ์ ์ฉํฉ๋๋ค.
Fallback Chain์ ์ฌ์ฉํ๋ ์ด์
- ํฅ์๋ ์ฌ์ฉ์ ๊ฒฝํ: UI ์์๊ฐ ์ฌ์ฉ ๊ฐ๋ฅํด์ง์ ๋ฐ๋ผ ์ ์ง์ ์ผ๋ก ํ์ํ์ฌ ๋ ๋ถ๋๋ฝ๊ณ ์ ์ตํ ๋ก๋ฉ ๊ฒฝํ์ ์ ๊ณตํฉ๋๋ค.
- ์ธ๋ถํ๋ ์ ์ด: ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ค๋ฅธ ๋ถ๋ถ์ ๋ํ ๋ก๋ฉ ์ํ๋ฅผ ์ธ๋ฐํ๊ฒ ์ ์ดํ ์ ์์ต๋๋ค.
- ์ธ์๋ ์ง์ฐ ์๊ฐ ๊ฐ์: ์ด๊ธฐ์ ๊ฐ๋จํ ๋ก๋ฉ ์ํ๋ฅผ ๋น ๋ฅด๊ฒ ํ์ํจ์ผ๋ก์จ ์ ์ฒด ๋ก๋ฉ ์๊ฐ์ด ๋์ผํ๋๋ผ๋ ์ฌ์ฉ์์ ์ธ์๋ ์ง์ฐ ์๊ฐ์ ์ค์ผ ์ ์์ต๋๋ค.
- ์ค๋ฅ ์ฒ๋ฆฌ: ์ค๋ฅ ๊ฒฝ๊ณ์ ๊ฒฐํฉํ์ฌ ์ปดํฌ๋ํธ ํธ๋ฆฌ์ ์ฌ๋ฌ ์์ค์์ ์ค๋ฅ๋ฅผ ์ฐ์ํ๊ฒ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
์์ ์๋๋ฆฌ์ค: ์ ์ ์๊ฑฐ๋ ์ ํ ํ์ด์ง
๋ค์๊ณผ ๊ฐ์ ๊ตฌ์ฑ ์์๊ฐ ์๋ ์ ์ ์๊ฑฐ๋ ์ ํ ํ์ด์ง๋ฅผ ๊ณ ๋ คํด ๋ณด์ธ์.
- ์ ํ ์ด๋ฏธ์ง
- ์ ํ ์ ๋ชฉ ๋ฐ ์ค๋ช
- ๊ฐ๊ฒฉ ๋ฐ ๊ฐ์ฉ์ฑ
- ๊ณ ๊ฐ ๋ฆฌ๋ทฐ
์ด๋ฌํ ๊ฐ ๊ตฌ์ฑ ์์๋ ๋ค๋ฅธ API์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ฑฐ๋ ๋ก๋ฉ ์๊ฐ์ด ๋ค๋ฅผ ์ ์์ต๋๋ค. fallback chain์ ์ฌ์ฉํ๋ฉด ๊ธฐ๋ณธ์ ์ธ ์ ํ ๊ณจ๊ฒฉ ๋ ์ด์์์ ๋น ๋ฅด๊ฒ ํ์ํ ๋ค์ ์ด๋ฏธ์ง, ์ธ๋ถ ์ ๋ณด ๋ฐ ๋ฆฌ๋ทฐ๊ฐ ์ฌ์ฉ ๊ฐ๋ฅํด์ง์ ๋ฐ๋ผ ์ ์ง์ ์ผ๋ก ๋ก๋ํ ์ ์์ต๋๋ค. ์ด๋ ๋น ํ์ด์ง๋ ๋จ์ผ ์ผ๋ฐ ๋ก๋ฉ ์คํผ๋๋ฅผ ํ์ํ๋ ๊ฒ๋ณด๋ค ํจ์ฌ ๋ ๋์ ์ฌ์ฉ์ ๊ฒฝํ์ ์ ๊ณตํฉ๋๋ค.
Fallback Chain ๊ตฌํ
React์์ fallback chain์ ๊ตฌํํ๋ ๋ฐฉ๋ฒ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
import React, { Suspense } from 'react';
// ํ๋ ์ด์คํ๋ ์ปดํฌ๋ํธ
const ProductImagePlaceholder = () => <div style={{ width: '200px', height: '200px', backgroundColor: '#eee' }}></div>;
const ProductDetailsPlaceholder = () => <div style={{ width: '300px', height: '50px', backgroundColor: '#eee' }}></div>;
const ReviewsPlaceholder = () => <div style={{ width: '400px', height: '100px', backgroundColor: '#eee' }}></div>;
// ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ ์ปดํฌ๋ํธ (์๋ฎฌ๋ ์ด์
)
const ProductImage = React.lazy(() => import('./ProductImage'));
const ProductDetails = React.lazy(() => import('./ProductDetails'));
const Reviews = React.lazy(() => import('./Reviews'));
function ProductPage() {
return (
<div>
<Suspense fallback={<ProductImagePlaceholder />}
<ProductImage productId="123" />
</Suspense>
<Suspense fallback={<ProductDetailsPlaceholder />}
<ProductDetails productId="123" />
</Suspense>
<Suspense fallback={<ReviewsPlaceholder />}
<Reviews productId="123" />
</Suspense>
</div>
);
}
export default ProductPage;
์ด ์์ ์์ ๊ฐ ์ปดํฌ๋ํธ(ProductImage, ProductDetails, Reviews)๋ ์์ฒด <Suspense> ์ปดํฌ๋ํธ๋ก ๋ํ๋ฉ๋๋ค. ์ด๋ฅผ ํตํด ๊ฐ ์ปดํฌ๋ํธ๋ ํด๋น ํ๋ ์ด์คํ๋๋ฅผ ๋ก๋ํ๋ ๋์ ํ์ํ๋ฉด์ ๋
๋ฆฝ์ ์ผ๋ก ๋ก๋ํ ์ ์์ต๋๋ค. React.lazy ํจ์๋ ์ฝ๋ ๋ถํ ์ ์ฌ์ฉ๋๋ฉฐ, ์ด๋ ํ์ํ ๋๋ง ์ปดํฌ๋ํธ๋ฅผ ๋ก๋ํ์ฌ ์ฑ๋ฅ์ ๋์ฑ ํฅ์์ํต๋๋ค. ์ด๊ฒ์ ๊ธฐ๋ณธ์ ์ธ ๊ตฌํ์ด๋ฉฐ, ์ค์ ์๋๋ฆฌ์ค์์๋ ํ๋ ์ด์คํ๋ ์ปดํฌ๋ํธ๋ฅผ ๋ ์๊ฐ์ ์ผ๋ก ๋งค๋ ฅ์ ์ธ ๋ก๋ฉ ํ์๊ธฐ(์ค์ผ๋ ํค ๋ก๋, ์คํผ๋ ๋ฑ)๋ก ๋ฐ๊พธ๊ณ ์๋ฎฌ๋ ์ด์
๋ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ๋ฅผ ์ค์ API ํธ์ถ๋ก ๋์ฒดํ ๊ฒ์
๋๋ค.
์ค๋ช :
React.lazy(): ์ด ํจ์๋ ์ฝ๋ ๋ถํ ์ ์ฌ์ฉ๋ฉ๋๋ค. ์ปดํฌ๋ํธ๋ฅผ ๋น๋๊ธฐ์ ์ผ๋ก ๋ก๋ํ ์ ์์ด ์ ํ๋ฆฌ์ผ์ด์ ์ ์ด๊ธฐ ๋ก๋ ์๊ฐ์ ๊ฐ์ ํ ์ ์์ต๋๋ค.React.lazy()๋ก ๋ํ๋ ์ปดํฌ๋ํธ๋ ์ฒ์ ๋ ๋๋ง๋ ๋๋ง ๋ก๋๋ฉ๋๋ค.<Suspense>๋ํผ: ๊ฐ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ ์ปดํฌ๋ํธ(ProductImage, ProductDetails, Reviews)๋<Suspense>์ปดํฌ๋ํธ๋ก ๋ํ๋ฉ๋๋ค. ์ด๋ Suspense๊ฐ ๊ฐ ์ปดํฌ๋ํธ์ ๋ก๋ฉ ์ํ๋ฅผ ๋ ๋ฆฝ์ ์ผ๋ก ์ฒ๋ฆฌํ๋๋ก ํ๋ ๋ฐ ์ค์ํฉ๋๋ค.fallbackprops: ๊ฐ<Suspense>์ปดํฌ๋ํธ์๋ ํด๋น ์ปดํฌ๋ํธ๊ฐ ๋ก๋๋๋ ๋์ ํ์ํ UI๋ฅผ ์ง์ ํ๋fallbackprop์ด ์์ต๋๋ค. ์ด ์์ ์์๋ ๊ฐ๋จํ ํ๋ ์ด์คํ๋ ์ปดํฌ๋ํธ(ProductImagePlaceholder, ProductDetailsPlaceholder, ReviewsPlaceholder)๋ฅผ fallback์ผ๋ก ์ฌ์ฉํ๊ณ ์์ต๋๋ค.- ๋
๋ฆฝ์ ๋ก๋ฉ: ๊ฐ ์ปดํฌ๋ํธ๊ฐ ์์ฒด
<Suspense>์ปดํฌ๋ํธ๋ก ๋ํ๋์ด ์๊ธฐ ๋๋ฌธ์ ๋ ๋ฆฝ์ ์ผ๋ก ๋ก๋๋ ์ ์์ต๋๋ค. ์ฆ, ProductImage๋ ProductDetails ๋๋ Reviews๊ฐ ๋ ๋๋ง๋๋ ๊ฒ์ ์ฐจ๋จํ์ง ์๊ณ ๋ก๋๋ ์ ์์ต๋๋ค. ์ด๋ ๋ ์ ์ง์ ์ด๊ณ ๋ฐ์์ ์ธ ์ฌ์ฉ์ ๊ฒฝํ์ผ๋ก ์ด์ด์ง๋๋ค.
๊ณ ๊ธ Fallback Chain ๊ธฐ์
์ค์ฒฉ๋ Suspense ๊ฒฝ๊ณ
<Suspense> ๊ฒฝ๊ณ๋ฅผ ์ค์ฒฉํ์ฌ ๋ ๋ณต์กํ ๋ก๋ฉ ์ํ ๊ณ์ธต ๊ตฌ์กฐ๋ฅผ ๋ง๋ค ์ ์์ต๋๋ค. ์:
import React, { Suspense } from 'react';
// ํ๋ ์ด์คํ๋ ์ปดํฌ๋ํธ
const OuterPlaceholder = () => <div style={{ width: '500px', height: '300px', backgroundColor: '#f0f0f0' }}></div>;
const InnerPlaceholder = () => <div style={{ width: '200px', height: '100px', backgroundColor: '#e0e0e0' }}></div>;
// ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ ์ปดํฌ๋ํธ (์๋ฎฌ๋ ์ด์
)
const OuterComponent = React.lazy(() => import('./OuterComponent'));
const InnerComponent = React.lazy(() => import('./InnerComponent'));
function App() {
return (
<Suspense fallback={<OuterPlaceholder />}
<OuterComponent>
<Suspense fallback={<InnerPlaceholder />}
<InnerComponent />
</Suspense>
</OuterComponent>
</Suspense>
);
}
export default App;
์ด ์์ ์์ InnerComponent๋ OuterComponent ๋ด๋ถ์ ์ค์ฒฉ๋ <Suspense> ์ปดํฌ๋ํธ๋ก ๋ํ๋๋ฉฐ, OuterComponent๋ <Suspense> ์ปดํฌ๋ํธ๋ก ๋ํ๋ฉ๋๋ค. ์ด๋ OuterComponent๊ฐ ๋ก๋๋๋ ๋์ OuterPlaceholder๊ฐ ํ์๋๊ณ , InnerComponent๊ฐ ๋ก๋๋๋ ๋์ (OuterComponent๊ฐ ๋ก๋๋ ํ) InnerPlaceholder๊ฐ ํ์๋๋ค๋ ๊ฒ์ ์๋ฏธํฉ๋๋ค. ์ด๋ฅผ ํตํด ์ ์ฒด ์ปดํฌ๋ํธ์ ๋ํ ์ผ๋ฐ ๋ก๋ฉ ํ์๊ธฐ์ ํ์ ์ปดํฌ๋ํธ์ ๋ํ ๋ ๊ตฌ์ฒด์ ์ธ ๋ก๋ฉ ํ์๊ธฐ๋ฅผ ํ์ํ ์ ์๋ ๋ค๋จ๊ณ ๋ก๋ฉ ๊ฒฝํ์ ์ป์ ์ ์์ต๋๋ค.
Suspense์ ์ค๋ฅ ๊ฒฝ๊ณ ์ฌ์ฉ
React ์ค๋ฅ ๊ฒฝ๊ณ๋ Suspense์ ํจ๊ป ์ฌ์ฉํ์ฌ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ ๋๋ ๋ ๋๋ง ์ค์ ๋ฐ์ํ๋ ์ค๋ฅ๋ฅผ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค. ์ค๋ฅ ๊ฒฝ๊ณ๋ ์์ ์ปดํฌ๋ํธ ํธ๋ฆฌ์์ JavaScript ์ค๋ฅ๋ฅผ catchํ๊ณ ํด๋น ์ค๋ฅ๋ฅผ ๊ธฐ๋กํ๋ฉฐ ์ ์ฒด ์ปดํฌ๋ํธ ํธ๋ฆฌ๊ฐ ์ถฉ๋ํ๋ ๋์ fallback UI๋ฅผ ํ์ํ๋ ์ปดํฌ๋ํธ์ ๋๋ค. ์ค๋ฅ ๊ฒฝ๊ณ์ Suspense๋ฅผ ๊ฒฐํฉํ๋ฉด fallback chain์ ์ฌ๋ฌ ์์ค์์ ์ค๋ฅ๋ฅผ ์ฐ์ํ๊ฒ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
import React, { Suspense } from 'react';
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// ๋ค์ ๋ ๋๋ง์์ fallback UI๋ฅผ ํ์ํ๋๋ก ์ํ๋ฅผ ์
๋ฐ์ดํธํฉ๋๋ค.
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// ์ค๋ฅ๋ฅผ ์ค๋ฅ ๋ณด๊ณ ์๋น์ค์ ๊ธฐ๋กํ ์๋ ์์ต๋๋ค.
console.error(error, errorInfo);
}
render() {
if (this.state.hasError) {
// ๋ชจ๋ ์ฌ์ฉ์ ์ง์ fallback UI๋ฅผ ๋ ๋๋งํ ์ ์์ต๋๋ค.
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
// ํ๋ ์ด์คํ๋ ์ปดํฌ๋ํธ
const ProductImagePlaceholder = () => <div style={{ width: '200px', height: '200px', backgroundColor: '#eee' }}></div>;
// ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ ์ปดํฌ๋ํธ (์๋ฎฌ๋ ์ด์
)
const ProductImage = React.lazy(() => import('./ProductImage'));
function ProductPage() {
return (
<ErrorBoundary>
<Suspense fallback={<ProductImagePlaceholder />}
<ProductImage productId="123" />
</Suspense>
</ErrorBoundary>
);
}
export default ProductPage;
์ด ์์ ์์ <ProductImage> ์ปดํฌ๋ํธ์ ๊ทธ <Suspense> ๋ํผ๋ <ErrorBoundary>๋ก ๋ํ๋ฉ๋๋ค. <ProductImage>์ ๋ ๋๋ง ์ค ๋๋ ๊ทธ ์์์์ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ ์ค์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ฉด <ErrorBoundary>๋ ์ค๋ฅ๋ฅผ catchํ๊ณ fallback UI(์ด ๊ฒฝ์ฐ ๊ฐ๋จํ "Something went wrong." ๋ฉ์์ง)๋ฅผ ํ์ํฉ๋๋ค. <ErrorBoundary>๊ฐ ์์ผ๋ฉด <ProductImage>์ ์ค๋ฅ๊ฐ ์ ์ฒด ์ ํ๋ฆฌ์ผ์ด์
์ ์ถฉ๋์ํฌ ์ ์์ต๋๋ค. <ErrorBoundary>์ <Suspense>๋ฅผ ๊ฒฐํฉํ๋ฉด ๋ก๋ฉ ์ํ์ ์ค๋ฅ ์กฐ๊ฑด์ ๋ชจ๋ ์ฐ์ํ๊ฒ ์ฒ๋ฆฌํ ์ ์๋ ๋ ๊ฒฌ๊ณ ํ๊ณ ํ๋ ฅ์ ์ธ ์ฌ์ฉ์ ์ธํฐํ์ด์ค๋ฅผ ๋ง๋ค ์ ์์ต๋๋ค.
์ฌ์ฉ์ ์ง์ Fallback ์ปดํฌ๋ํธ
๊ฐ๋จํ ๋ก๋ฉ ์คํผ๋๋ ํ๋ ์ด์คํ๋ ์์ ๋์ ๋ ์ ๊ตํ fallback ์ปดํฌ๋ํธ๋ฅผ ๋ง๋ค์ด ์ฌ์ฉ์ ๊ฒฝํ์ ๊ฐ์ ํ ์ ์์ต๋๋ค. ๋ค์์ ๊ณ ๋ คํด ๋ณด์ธ์.
- ์ค์ผ๋ ํค ๋ก๋: ์ค์ ์ฝํ ์ธ ์ ๋ ์ด์์์ ์๋ฎฌ๋ ์ด์ ํ์ฌ ๋ก๋๋ ๋ด์ฉ์ ๋ํ ์๊ฐ์ ํ์๋ฅผ ์ ๊ณตํฉ๋๋ค.
- ์งํ๋ฅ ํ์์ค: ๊ฐ๋ฅํ ๊ฒฝ์ฐ ๋ฐ์ดํฐ ๋ก๋ ์งํ๋ฅ ์ ํ์ํฉ๋๋ค.
- ์ ์ตํ ๋ฉ์์ง: ๋ฌด์์ด ๋ก๋๋๊ณ ์๋์ง, ๊ทธ๋ฆฌ๊ณ ์๊ฐ์ด ๊ฑธ๋ฆฌ๋ ์ด์ ์ ๋ํ ๋งฅ๋ฝ์ ์ ๊ณตํฉ๋๋ค.
์๋ฅผ ๋ค์ด "Loading..."์ ํ์ํ๋ ๋์ "Fetching product details..." ๋๋ "Loading customer reviews..."๋ฅผ ํ์ํ ์ ์์ต๋๋ค. ํต์ฌ์ ์ฌ์ฉ์์๊ฒ ๊ด๋ จ ์ ๋ณด๋ฅผ ์ ๊ณตํ์ฌ ๊ธฐ๋์น๋ฅผ ๊ด๋ฆฌํ๋ ๊ฒ์ ๋๋ค.
React Suspense Fallback Chain ์ฌ์ฉ ๋ชจ๋ฒ ์ฌ๋ก
- ๊ธฐ๋ณธ Fallback์ผ๋ก ์์: ๋น ํ๋ฉด์ ๋ฐฉ์งํ๊ธฐ ์ํด ๊ฐ๋ฅํ ํ ๋นจ๋ฆฌ ๊ฐ๋จํ ๋ก๋ฉ ํ์๊ธฐ๋ฅผ ํ์ํฉ๋๋ค.
- Fallback ์ ์ง์ ํฅ์: ๋ ๋ง์ ์ ๋ณด๊ฐ ์ฌ์ฉ ๊ฐ๋ฅํด์ง์ ๋ฐ๋ผ fallback UI๋ฅผ ์ ๋ฐ์ดํธํ์ฌ ๋ ๋ง์ ์ปจํ ์คํธ๋ฅผ ์ ๊ณตํฉ๋๋ค.
- ์ฝ๋ ๋ถํ ์ฌ์ฉ: Suspense์
React.lazy()๋ฅผ ๊ฒฐํฉํ์ฌ ํ์ํ ์ปดํฌ๋ํธ๋ง ๋ก๋ํ์ฌ ์ด๊ธฐ ๋ก๋ ์๊ฐ์ ๊ฐ์ ํฉ๋๋ค. - ์ค๋ฅ ์ฐ์ํ๊ฒ ์ฒ๋ฆฌ: ์ค๋ฅ ๊ฒฝ๊ณ๋ฅผ ์ฌ์ฉํ์ฌ ์ค๋ฅ๋ฅผ catchํ๊ณ ์ ์ตํ ์ค๋ฅ ๋ฉ์์ง๋ฅผ ํ์ํฉ๋๋ค.
- ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ ์ต์ ํ: ํจ์จ์ ์ธ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ ๊ธฐ์ (์: ์บ์ฑ, ์ค๋ณต ์ ๊ฑฐ)์ ์ฌ์ฉํ์ฌ ๋ก๋ฉ ์๊ฐ์ ์ต์ํํฉ๋๋ค.
react-query๋ฐswr๊ณผ ๊ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ์ด๋ฌํ ๊ธฐ์ ์ ๋ํ ๊ธฐ๋ณธ ์ง์์ ์ ๊ณตํฉ๋๋ค. - ์ฑ๋ฅ ๋ชจ๋ํฐ๋ง: React DevTools๋ฅผ ์ฌ์ฉํ์ฌ Suspense ์ปดํฌ๋ํธ์ ์ฑ๋ฅ์ ๋ชจ๋ํฐ๋งํ๊ณ ์ ์ฌ์ ๋ณ๋ชฉ ํ์์ ์๋ณํฉ๋๋ค.
- ์ ๊ทผ์ฑ ๊ณ ๋ ค: fallback UI๊ฐ ์ฅ์ ๊ฐ ์๋ ์ฌ์ฉ์์๊ฒ ์ ๊ทผ ๊ฐ๋ฅํ์ง ํ์ธํฉ๋๋ค. ๋ก๋ฉ ์ค์ธ ์ฝํ ์ธ ๋ฅผ ๋ํ๋ด๊ธฐ ์ํด ์ ์ ํ ARIA ์์ฑ์ ์ฌ์ฉํ๊ณ ๋ก๋ฉ ํ์๊ธฐ์ ๋ํ ๋์ฒด ํ ์คํธ๋ฅผ ์ ๊ณตํฉ๋๋ค.
๋ก๋ฉ ์ํ์ ๋ํ ์ ์ญ ๊ณ ๋ ค ์ฌํญ
์ ์ธ๊ณ ์ฌ์ฉ์๋ฅผ ์ํด ๊ฐ๋ฐํ ๋๋ ๋ก๋ฉ ์ํ์ ๊ด๋ จ๋ ๋ค์ ์์๋ฅผ ๊ณ ๋ คํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค.
- ๋ค์ํ ๋คํธ์ํฌ ์๋: ์ ์ธ๊ณ์ ์ฌ์ฉ์๋ ๋คํธ์ํฌ ์๋์ ์๋นํ ์ฐจ์ด๊ฐ ์์ ์ ์์ต๋๋ค. ๋ก๋ฉ ์ํ๋ ๋๋ฆฐ ์ฐ๊ฒฐ์ ์์ฉํ๋๋ก ์ค๊ณํด์ผ ํฉ๋๋ค. ์ ์ง์ ์ด๋ฏธ์ง ๋ก๋ฉ ๋ฐ ๋ฐ์ดํฐ ์์ถ๊ณผ ๊ฐ์ ๊ธฐ์ ์ ์ฌ์ฉํ์ฌ ์ ์กํด์ผ ํ๋ ๋ฐ์ดํฐ ์์ ์ค์ด๋ ๊ฒ์ ๊ณ ๋ คํ์ญ์์ค.
- ์๊ฐ๋: ๋ก๋ฉ ์ํ์์ ์๊ฐ ๊ด๋ จ ์ ๋ณด(์: ์์ ์๋ฃ ์๊ฐ)๋ฅผ ํ์ํ ๋ ์ฌ์ฉ์์ ์๊ฐ๋๋ฅผ ๋ฐ๋์ ๊ณ ๋ คํ์ญ์์ค.
- ์ธ์ด ๋ฐ ํ์งํ: ๋ชจ๋ ๋ก๋ฉ ๋ฉ์์ง์ ํ์๊ธฐ๊ฐ ๋ค์ํ ์ธ์ด ๋ฐ ์ง์ญ์ ๋ํด ์ฌ๋ฐ๋ฅด๊ฒ ๋ฒ์ญ๋๊ณ ํ์งํ๋์๋์ง ํ์ธํ์ญ์์ค.
- ๋ฌธํ์ ๋ฏผ๊ฐ์ฑ: ํน์ ์ฌ์ฉ์์๊ฒ ๋ถ์พํ๊ฑฐ๋ ๋ฌธํ์ ์ผ๋ก ๋ฏผ๊ฐํ ์ ์๋ ๋ก๋ฉ ํ์๊ธฐ๋ ๋ฉ์์ง๋ฅผ ์ฌ์ฉํ์ง ์๋๋ก ํ์ญ์์ค. ์๋ฅผ ๋ค์ด ํน์ ์์์ด๋ ๊ธฐํธ๋ ๋ค๋ฅธ ๋ฌธํ์์ ๋ค๋ฅธ ์๋ฏธ๋ฅผ ๊ฐ์ง ์ ์์ต๋๋ค.
- ์ ๊ทผ์ฑ: ์คํฌ๋ฆฐ ๋ฆฌ๋๋ฅผ ์ฌ์ฉํ์ฌ ๋ก๋ฉ ์ํ๊ฐ ์ฅ์ ๊ฐ ์๋ ์ฌ๋๋ค์๊ฒ ์ ๊ทผ ๊ฐ๋ฅํ์ง ํ์ธํ์ญ์์ค. ์ถฉ๋ถํ ์ ๋ณด๋ฅผ ์ ๊ณตํ๊ณ ARIA ์์ฑ์ ์ฌ๋ฐ๋ฅด๊ฒ ์ฌ์ฉํ์ญ์์ค.
์ค์ ์์
React Suspense fallback chain์ ์ฌ์ฉํ์ฌ ์ฌ์ฉ์ ๊ฒฝํ์ ๊ฐ์ ํ๋ ๋ฐฉ๋ฒ์ ๋ํ ๋ช ๊ฐ์ง ์ค์ ์๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
- ์์ ๋ฏธ๋์ด ํผ๋: ์ค์ ์ฝํ ์ธ ๊ฐ ๋ก๋๋๋ ๋์ ๊ฒ์๋ฌผ์ ๋ํ ๊ธฐ๋ณธ ์ค์ผ๋ ํค ๋ ์ด์์์ ํ์ํฉ๋๋ค.
- ๋์๋ณด๋: ๊ฐ ์์ ฏ๊ณผ ์ฐจํธ๋ฅผ ๋ ๋ฆฝ์ ์ผ๋ก ๋ก๋ํ๊ณ ๋ก๋๋๋ ๋์ ๊ฐ ์์ ฏ์ ๋ํ ํ๋ ์ด์คํ๋๋ฅผ ํ์ํฉ๋๋ค.
- ์ด๋ฏธ์ง ๊ฐค๋ฌ๋ฆฌ: ๊ณ ํด์๋ ๋ฒ์ ์ ์ด๋ฏธ์ง๊ฐ ๋ก๋๋๋ ๋์ ์ ํด์๋ ๋ฒ์ ์ ํ์ํฉ๋๋ค.
- ์ ์ ํ์ต ํ๋ซํผ: ๋น๋์ค, ํ ์คํธ ๋ฐ ๋ํํ ์์์ ๋ํ ํ๋ ์ด์คํ๋๋ฅผ ํ์ํ์ฌ ๊ฐ์ ์ฝํ ์ธ ์ ํด์ฆ๋ฅผ ์ ์ง์ ์ผ๋ก ๋ก๋ํฉ๋๋ค.
๊ฒฐ๋ก
React Suspense fallback chain์ ์ ํ๋ฆฌ์ผ์ด์ ์์ ๋ก๋ฉ ์ํ๋ฅผ ๊ด๋ฆฌํ๋ ๊ฐ๋ ฅํ๊ณ ์ ์ฐํ ๋ฐฉ๋ฒ์ ์ ๊ณตํฉ๋๋ค. fallback ์ปดํฌ๋ํธ์ ๊ณ์ธต ๊ตฌ์กฐ๋ฅผ ๋ง๋ฆ์ผ๋ก์จ ๋ ๋ถ๋๋ฝ๊ณ ์ ์ตํ ์ฌ์ฉ์ ๊ฒฝํ์ ์ ๊ณตํ์ฌ ์ธ์๋ ์ง์ฐ ์๊ฐ์ ์ค์ด๊ณ ์ ๋ฐ์ ์ธ ์ฐธ์ฌ๋๋ฅผ ํฅ์์ํฌ ์ ์์ต๋๋ค. ์ด ๋ธ๋ก๊ทธ ๊ฒ์๋ฌผ์ ์ค๋ช ๋ ๋ชจ๋ฒ ์ฌ๋ก๋ฅผ ๋ฐ๋ฅด๊ณ ์ ์ญ ์์๋ฅผ ๊ณ ๋ คํ๋ฉด ๋ค์ํ ์ฌ์ฉ์์๊ฒ ์ ํฉํ ๊ฒฌ๊ณ ํ๊ณ ์ฌ์ฉ์ ์นํ์ ์ธ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ง๋ค ์ ์์ต๋๋ค. React Suspense์ ํ์ ํ์ฉํ๊ณ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ก๋ฉ ์ํ์ ๋ํ ์ ์ด ์์ค์ ํ ๋จ๊ณ ๋์ด์ญ์์ค.
Suspense์ ์ ์ ์๋ fallback chain์ ์ ๋ต์ ์ผ๋ก ์ฌ์ฉํจ์ผ๋ก์จ ๊ฐ๋ฐ์๋ ์ฌ์ฉ์ ๊ฒฝํ์ ํฌ๊ฒ ํฅ์์์ผ ๋ณต์กํ ๋ฐ์ดํฐ ์ข ์์ฑ ๋ฐ ๋ค์ํ ๋คํธ์ํฌ ์กฐ๊ฑด์์๋ ๋ ๋น ๋ฅด๊ณ ๋ฐ์์ฑ์ด ๋ฐ์ด๋๋ฉฐ ์ฌ์ฉ์ ์นํ์ ์ธ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ง๋ค ์ ์์ต๋๋ค.